﻿//==============================================================================  
//Copyright (C) 2012-2015 9UN.ORG. All rights reserved. 
//GUID：2bbcfc56-a115-4b53-90ca-4a71749a3c1c
//CLR Version: 4.0.30319.18033
//Code Author：Kevin Wang
//Contact：Email(Admin@9un.org),QQ(265382 or 74344)
//Filename：LibVlcMediaInterop
//Namespace：RuanRang.Media.Core.Internal.Interop
//Functions：LibVlcMediaInterop  
//Created by Kevin Wang at 2013/3/3 15:42:57 http://blog.9un.org
//============================================================================== 
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;
using RuanRang.Media.Core.Exceptions;
using RuanRang.Media.Core.Internal.DataStructures;
using log4net;

namespace RuanRang.Media.Core.Internal.Interop
{
    internal class LibVlcMediaInterop : BaseInterop
    {
        /// <summary>
        /// Initializes static members of the <see cref="LibVlcMediaInterop"/> class.
        /// </summary>
        static LibVlcMediaInterop()
        {
            Logger = LogManager.GetLogger(typeof(LibVlcMediaInterop));
        }

        #region Media

        /// <summary>
        /// Create a media with the given MRL
        /// </summary>
        /// <param name="p_instance">libvlc instance </param>
        /// <param name="psz_mrl">MRL</param>
        /// <returns>Media descriptor</returns>
        [DllImport("libvlc", ExactSpelling = true, EntryPoint = "libvlc_media_new_path")]
        [SuppressUnmanagedCodeSecurity]
        private static extern IntPtr libvlc_media_new_path_unmanaged(IntPtr p_instance, IntPtr psz_mrl);

        /// <summary>
        /// Libvlc_media_new_pathes the specified instance.
        /// </summary>
        /// <param name="instance">The instance.</param>
        /// <param name="path">The path.</param>
        /// <returns></returns>
        [DllImport("libvlc", ExactSpelling = true, EntryPoint = "libvlc_media_new_path")]
        [SuppressUnmanagedCodeSecurity]
        public static extern IntPtr libvlc_media_new_path(IntPtr p_instance, byte[] psz_mrl);


        /// <summary>
        /// Libvlc_media_new_locations the specified instance.
        /// </summary>
        /// <param name="p_instance">The p_instance.</param>
        /// <param name="mrl">The MRL.</param>
        /// <returns></returns>
        [DllImport("libvlc", ExactSpelling = true, EntryPoint = "libvlc_media_new_location", CallingConvention = CallingConvention.Cdecl)]
        [SuppressUnmanagedCodeSecurity]
        public static extern IntPtr libvlc_media_new_location(IntPtr p_instance, string mrl);

        /// <summary>
        /// Libvlc_media_new_locations the specified p_instance.
        /// </summary>
        /// <param name="p_instance">The p_instance.</param>
        /// <param name="mrl">The MRL.</param>
        /// <returns>IntPtr.</returns>
        [DllImport("libvlc", ExactSpelling = true, EntryPoint = "libvlc_media_new_location", CallingConvention = CallingConvention.Cdecl)]
        [SuppressUnmanagedCodeSecurity]
        public static extern IntPtr libvlc_media_new_location(IntPtr p_instance, byte[] mrl);
        /// <summary>
        /// Create a media with the given MRL
        /// Helper to the original function, <paramref name="mrl"/> string will be
        /// converted to the UTF-8 encoding automatically and marshalled to the libvlc.
        /// </summary>
        /// <param name="p_instance">libvlc instance </param>
        /// <param name="mrl">MRL</param>
        public static IntPtr libvlc_media_new_path(IntPtr p_instance, string mrl)
        {
            if (CallTraceEnabled)
            {
                TraceCall("libvlc_media_new_path");
            }
            var pMrl = IntPtr.Zero;
            try
            {
                var bytes = Encoding.UTF8.GetBytes(mrl);
                //
                pMrl = Marshal.AllocHGlobal(bytes.Length + 1);
                Marshal.Copy(bytes, 0, pMrl, bytes.Length);
                Marshal.WriteByte(pMrl, bytes.Length, 0);
                //
                return (libvlc_media_new_path_unmanaged(p_instance, pMrl));
            }
            finally
            {
                if (pMrl != IntPtr.Zero)
                {
                    Marshal.FreeHGlobal(pMrl);
                }
            }
        }

        /// <summary>
        /// Libvlc_media_parse_unmanageds the specified media.
        /// </summary>
        /// <param name="media">The media.</param>
        [DllImport("libvlc", ExactSpelling = true, EntryPoint = "libvlc_media_parse")]
        [SuppressUnmanagedCodeSecurity]
        private static extern void libvlc_media_parse_unmanaged(IntPtr media);

        /// <summary>
        /// Parse a media.
        /// </summary>
        /// <param name="media">A <see cref="IntPtr" /> pointer to libvlc_media_t.</param>
        public static void libvlc_media_parse(IntPtr media)
        {
            if (CallTraceEnabled)
            {
                TraceCall("libvlc_media_parse");
            }
            libvlc_media_parse_unmanaged(media);
        }

        /// <summary>
        /// Libvlc_media_parse_async_unmanageds the specified media.
        /// </summary>
        /// <param name="media">The media.</param>
        [DllImport("libvlc", ExactSpelling = true, EntryPoint = "libvlc_media_parse_async")]
        [SuppressUnmanagedCodeSecurity]
        private static extern void libvlc_media_parse_async_unmanaged(IntPtr media);

        /// <summary>
        /// Parse a media asynchronously.
        /// </summary>
        /// <param name="media">
        /// A <see cref="IntPtr"/> pointer to libvlc_media_t.
        /// </param>
        public static void libvlc_media_parse_async(IntPtr media)
        {
            if (CallTraceEnabled)
            {
                TraceCall("libvlc_media_parse_async");
            }
            libvlc_media_parse_async_unmanaged(media);
        }

        [DllImport("libvlc", ExactSpelling = true, EntryPoint = "libvlc_media_is_parsed")]
        [SuppressUnmanagedCodeSecurity]
        private static extern int libvlc_media_is_parsed_unmanaged(IntPtr media);

        /// <summary>
        /// Get parsed status for media descriptor object.
        /// </summary>
        /// <param name="media">
        /// A <see cref="IntPtr"/> pointer to libvlc_media_t.
        /// </param>
        /// <returns>
        /// A <see cref="bool"/> value will be <code>true</code> if media 
        /// is parsed and <code>false</code> if not.
        /// </returns>
        public static bool libvlc_media_is_parsed(IntPtr media)
        {
            if (CallTraceEnabled)
            {
                TraceCall("libvlc_media_is_parsed");
            }
            var res = libvlc_media_is_parsed_unmanaged(media);
            return res != 0;
        }

        /// <summary>
        /// Libvlc_media_get_duration_unmanageds the specified media.
        /// </summary>
        /// <param name="media">The media.</param>
        /// <returns>Int64.</returns>
        [DllImport("libvlc", ExactSpelling = true, EntryPoint = "libvlc_media_get_duration")]
        [SuppressUnmanagedCodeSecurity]
        private static extern Int64 libvlc_media_get_duration_unmanaged(IntPtr media);

        /// <summary>
        /// Libvlc_media_get_durations the specified media.
        /// </summary>
        /// <param name="media">The media.</param>
        /// <returns>Int64.</returns>
        public static Int64 libvlc_media_get_duration(IntPtr media)
        {
            if (CallTraceEnabled)
            {
                TraceCall("libvlc_media_is_parsed");
            }
            return libvlc_media_get_duration_unmanaged(media);
        }

        /// <summary>
        /// Libvlc_media_get_tracks_info_unmanageds the specified media.
        /// </summary>
        /// <param name="media">The media.</param>
        /// <param name="tracks">The tracks.</param>
        /// <returns>System.Int32.</returns>
        [DllImport("libvlc", ExactSpelling = true, EntryPoint = "libvlc_media_get_tracks_info")]
        [SuppressUnmanagedCodeSecurity]
        private static extern int libvlc_media_get_tracks_info_unmanaged(IntPtr media, IntPtr tracks);

        /// <summary>
        /// Libvlc_frees the specified PTR.
        /// </summary>
        /// <param name="ptr">The PTR.</param>
        /// <returns>System.Int32.</returns>
        [DllImport("libvlc", ExactSpelling = true, EntryPoint = "free")]
        [SuppressUnmanagedCodeSecurity]
        public static extern int libvlc_free(IntPtr ptr);

        /// <summary>
        /// Vlc_fourcc_s the get description.
        /// </summary>
        /// <param name="i_cat">The i_cat.</param>
        /// <param name="i_fourcc">The i_fourcc.</param>
        /// <returns>System.String.</returns>
        public static string vlc_fourcc_GetDescription(int i_cat, uint i_fourcc)
        {
            if (CallTraceEnabled)
            {
                TraceCall("vlc_fourcc_GetDescription");
            }
            var strPtr = vlc_fourcc_GetDescription_unmanaged(i_cat, i_fourcc);
            return Marshal.PtrToStringAnsi(strPtr);
        }

        /// <summary>
        /// Vlc_fourcc_s the get description_unmanaged.
        /// </summary>
        /// <param name="i_cat">The i_cat.</param>
        /// <param name="i_fourcc">The i_fourcc.</param>
        /// <returns>IntPtr.</returns>
        [DllImport("libvlccore", ExactSpelling = true, EntryPoint = "vlc_fourcc_GetDescription")]
        [SuppressUnmanagedCodeSecurity]
        private static extern IntPtr vlc_fourcc_GetDescription_unmanaged(int i_cat, uint i_fourcc);

        /// <summary>
        /// Libvlc_media_get_tracks_infoes the specified media.
        /// </summary>
        /// <param name="media">The media.</param>
        /// <returns>libvlc_media_track_info_t[][].</returns>
        public static libvlc_media_track_info_t[] libvlc_media_get_tracks_info(IntPtr media)
        {
            if (CallTraceEnabled)
            {
                TraceCall("libvlc_media_get_tracks_info");
            }
            var size = Marshal.SizeOf(typeof (libvlc_media_track_info_t));
            var pointerSize = IntPtr.Size;
            var pointer = Marshal.AllocHGlobal(pointerSize);
            for (var i = 0; i < pointerSize; i++)
            {
                Marshal.WriteByte(new IntPtr(pointer.ToInt64() + i), 0x0);
            }
            var count = libvlc_media_get_tracks_info_unmanaged(media, pointer);
            if (count > 0)
            {
                var list = new List<libvlc_media_track_info_t>(count);
                var arrPtr = Marshal.ReadIntPtr(pointer);
                if (arrPtr != IntPtr.Zero)
                {
                    for (var i = 0; i < count; i++)
                    {
                        var structure = new libvlc_media_track_info_t();
                        Marshal.PtrToStructure(arrPtr, structure);
                        list.Add(structure);
                        arrPtr = new IntPtr(arrPtr.ToInt64() + size);
                    }
                    return list.ToArray();
                }
            }
            Marshal.FreeHGlobal(pointer);
            return new libvlc_media_track_info_t[] {};
        }

        /// <summary>
        /// Add an option to the media
        /// </summary>
        [DllImport("libvlc", ExactSpelling = true, EntryPoint = "libvlc_media_add_option")]
        [SuppressUnmanagedCodeSecurity]
        private static extern void libvlc_media_add_option_unmanaged(IntPtr libvlc_media_inst, IntPtr ppsz_options);

        /// <summary>
        /// Adds an options in MRL's option format
        /// </summary>
        /// <param name="libvlc_media_inst">Media instance</param>
        /// <param name="options">Options string ("::sout=#transcode{vcodec=DIV3,vb=1024,scale=1}:duplicate{dst=display,dst=std{access=file,mux=ps,dst=\"C:\\temp.mpeg\"}}" for example)</param>
        public static void libvlc_media_add_option(IntPtr libvlc_media_inst, string options)
        {
            if (CallTraceEnabled)
            {
                TraceCall("libvlc_media_add_option");
            }
            var pOptions = IntPtr.Zero;
            try
            {
                pOptions = Marshal.StringToHGlobalAnsi(options);

                libvlc_media_add_option_unmanaged(libvlc_media_inst, pOptions);
            }
            finally
            {
                if (pOptions != IntPtr.Zero)
                {
                    Marshal.FreeHGlobal(pOptions);
                }
            }
        }

        /// <summary>
        /// Libvlc_media_get_mrl_unmanageds the specified libvlc_media_inst.
        /// </summary>
        /// <param name="libvlc_media_inst">The libvlc_media_inst.</param>
        /// <returns>IntPtr.</returns>
        [DllImport("libvlc", ExactSpelling = true, EntryPoint = "libvlc_media_get_mrl")]
        [SuppressUnmanagedCodeSecurity]
        private static extern IntPtr libvlc_media_get_mrl_unmanaged(IntPtr libvlc_media_inst);

        /// <summary>
        /// Libvlc_media_get_mrls the specified libvlc_media_inst.
        /// </summary>
        /// <param name="libvlc_media_inst">The libvlc_media_inst.</param>
        /// <returns>System.String.</returns>
        /// <exception cref="RuanRang.Media.Core.Exceptions.VlcInternalException"></exception>
        public static string libvlc_media_get_mrl(IntPtr libvlc_media_inst)
        {
            if (CallTraceEnabled)
            {
                TraceCall("libvlc_media_get_mrl");
            }
            var result = libvlc_media_get_mrl_unmanaged(libvlc_media_inst);
            if (result == IntPtr.Zero)
            {
                throw new VlcInternalException(LibVlcCoreInterop.libvlc_errmsg());
            }
            return Marshal.PtrToStringAnsi(result);
        }

        /// <summary>
        /// Decrement the reference count of a media descriptor object
        /// </summary>
        /// <param name="libvlc_media_inst">Media descriptor</param>
        [DllImport("libvlc", ExactSpelling = true, EntryPoint = "libvlc_media_release")]
        [SuppressUnmanagedCodeSecurity]
        private static extern void libvlc_media_release_unmanaged(IntPtr libvlc_media_inst);

        /// <summary>
        /// Libvlc_media_releases the specified libvlc_media_inst.
        /// </summary>
        /// <param name="libvlc_media_inst">The libvlc_media_inst.</param>
        public static void libvlc_media_release(IntPtr libvlc_media_inst)
        {
            if (CallTraceEnabled)
            {
                TraceCall("libvlc_media_release");
            }
            libvlc_media_release_unmanaged(libvlc_media_inst);
        }

        /// <summary>
        /// Increment the reference count of a media descriptor object
        /// </summary>
        /// <param name="libvlc_media_inst">Media descriptor</param>
        [DllImport("libvlc", ExactSpelling = true, EntryPoint = "libvlc_media_retain")]
        [SuppressUnmanagedCodeSecurity]
        private static extern IntPtr libvlc_media_retain_unmanaged(IntPtr libvlc_media_inst);

        /// <summary>
        /// Libvlc_media_retains the specified libvlc_media_inst.
        /// </summary>
        /// <param name="libvlc_media_inst">The libvlc_media_inst.</param>
        /// <returns>IntPtr.</returns>
        public static IntPtr libvlc_media_retain(IntPtr libvlc_media_inst)
        {
            if (CallTraceEnabled)
            {
                TraceCall("libvlc_media_retain");
            }
            return libvlc_media_retain_unmanaged(libvlc_media_inst);
        }

        /// <summary>
        /// Get current state of media descriptor object
        /// </summary>
        /// <param name="libvlc_media_inst">Media descriptor</param>
        /// <returns>libvlc_state_t enum</returns>
        [DllImport("libvlc", ExactSpelling = true, EntryPoint = "libvlc_media_get_state")]
        [SuppressUnmanagedCodeSecurity]
        private static extern VlcMediaState libvlc_media_get_state_unmanaged(IntPtr libvlc_media_inst);

        /// <summary>
        /// Libvlc_media_get_states the specified libvlc_media_inst.
        /// </summary>
        /// <param name="libvlc_media_inst">The libvlc_media_inst.</param>
        /// <returns>libvlc_state_t.</returns>
        public static VlcMediaState libvlc_media_get_state(IntPtr libvlc_media_inst)
        {
            if (CallTraceEnabled)
            {
                TraceCall("libvlc_media_get_state");
            }
            return libvlc_media_get_state_unmanaged(libvlc_media_inst);
        }

        /// <summary>
        /// Libvlc_media_event_manager_unmanageds the specified libvlc_media_t.
        /// </summary>
        /// <param name="libvlc_media_t">The libvlc_media_t.</param>
        /// <returns>IntPtr.</returns>
        [DllImport("libvlc", ExactSpelling = true, EntryPoint = "libvlc_media_event_manager")]
        [SuppressUnmanagedCodeSecurity]
        private static extern IntPtr libvlc_media_event_manager_unmanaged(IntPtr libvlc_media_t);

        /// <summary>
        /// Libvlc_media_event_managers the specified libvlc_media_t.
        /// </summary>
        /// <param name="libvlc_media_t">The libvlc_media_t.</param>
        /// <returns>IntPtr.</returns>
        public static IntPtr libvlc_media_event_manager(IntPtr libvlc_media_t)
        {
            if (CallTraceEnabled)
            {
                TraceCall("libvlc_media_event_manager");
            }
            return libvlc_media_event_manager_unmanaged(libvlc_media_t);
        }

        /// <summary>
        /// Create a media for an already open file descriptor.
        /// </summary>
        /// <param name="instance">The instance</param>
        /// <param name="fd">Open file descriptor</param>
        /// <returns>Newly created media or NULL on error</returns>
        [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl)]
        public static extern IntPtr libvlc_media_new_fd(IntPtr instance, int fd);

        /// <summary>
        /// Create a media as an empty node with a given name.
        /// </summary>
        /// <param name="instance">The instance</param>
        /// <param name="name">The name of the node</param>
        /// <returns>New empty media or NULL on error</returns>
        [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl)]
        public static extern IntPtr libvlc_media_new_as_node(IntPtr instance, string name);

        /// <summary>
        /// Add an option to the media with configurable flags.
        /// </summary>
        /// <param name="mediaInstance">media descriptor</param>
        /// <param name="options">options (as a string)</param>
        /// <param name="flags">flags for this option</param>
        [DllImport("libvlc")]
        public static extern void libvlc_media_add_option_flag(IntPtr mediaInstance, string options, uint flags);

        /// <summary>
        /// Duplicate a media descriptor object.
        /// </summary>
        /// <param name="mediaInstance">media descriptor object.</param>
        /// <returns></returns>
        [DllImport("libvlc")]
        public static extern IntPtr libvlc_media_duplicate(IntPtr mediaInstance);

        /// <summary>
        /// Read the meta of the media.
        /// </summary>
        /// <param name="mediaInstance">Media descriptor object</param>
        /// <param name="meta">Meta to read</param>
        /// <returns></returns>
        [DllImport("libvlc")]
        public static extern IntPtr libvlc_media_get_meta(IntPtr mediaInstance, libvlc_meta_t meta);

        /// <summary>
        /// Set the meta of the media (this function will not save the meta, call libvlc_media_save_meta in order to save the meta)
        /// </summary>
        /// <param name="mediaInstance">Media descriptor object</param>
        /// <param name="meta">Meta to write</param>
        /// <param name="value">Media's meta</param>
        [DllImport("libvlc")]
        public static extern void libvlc_media_set_meta(IntPtr mediaInstance, libvlc_meta_t meta, string value);

        /// <summary>
        /// Save the meta previously set.
        /// </summary>
        /// <param name="mediaInstance">Media descriptor object</param>
        /// <returns>True if the write operation was successfull</returns>
        [DllImport("libvlc")]
        public static extern bool libvlc_media_save_meta(IntPtr mediaInstance);

        /// <summary>
        /// Get the current statistics about the media.
        /// </summary>
        /// <param name="mediaInstance">Media descriptor object</param>
        /// <param name="stats">Structure that contain the statistics about the media</param>
        /// <returns></returns>
        [DllImport("libvlc")]
        public static extern bool libvlc_media_get_stats(IntPtr mediaInstance, ref libvlc_media_stats_t stats);

        /// <summary>
        /// Get subitems of media descriptor object.
        /// </summary>
        /// <param name="mediaInstance">Media descriptor object</param>
        /// <returns>List of media descriptor subitems or NULL</returns>
        [DllImport("libvlc")]
        public static extern IntPtr libvlc_media_subitems(IntPtr mediaInstance);

        /// <summary>
        /// Sets media descriptor's user_data. user_data is specialized data accessed by the host application, VLC.framework uses it as a pointer to an native object that references a libvlc_media_t pointer
        /// </summary>
        /// <param name="mediaInstance">Media descriptor object</param>
        /// <param name="newUserData">Pointer to user data</param>
        [DllImport("libvlc")]
        public static extern void libvlc_media_set_user_data(IntPtr mediaInstance, IntPtr newUserData);

        /// <summary>
        /// Get media descriptor's user_data. user_data is specialized data accessed by the host application, VLC.framework uses it as a pointer to an native object that references a libvlc_media_t pointer
        /// </summary>
        /// <param name="mediaInstance">Media descriptor object</param>
        /// <returns>Pointer to user data</returns>
        [DllImport("libvlc")]
        public static extern IntPtr libvlc_media_get_user_data(IntPtr mediaInstance);

        /// <summary>
        /// Get media descriptor's elementary streams description.
        /// </summary>
        /// <param name="mediaInstance">Media descriptor object</param>
        /// <param name="tracks">Array of Elementary Streams descriptions (must be freed by the caller)</param>
        /// <returns>Number of Elementary Streams</returns>
        [DllImport("libvlc")]
        public static extern int libvlc_media_get_tracks_info(IntPtr mediaInstance, libvlc_media_track_info_t[] tracks);

        /// <summary>
        /// Libvlc_media_get_tracks_infoes the specified media instance.
        /// </summary>
        /// <param name="mediaInstance">The media instance.</param>
        /// <param name="trackInfo">The track info.</param>
        /// <returns></returns>
        [DllImport("libvlc", CallingConvention = CallingConvention.Cdecl)]
        public static extern int libvlc_media_get_tracks_info(IntPtr mediaInstance, out IntPtr trackInfo);
        #endregion
    }
}
